module alu(
  input  wire clk,
  input  wire reset,
  input  wire [18:0] alu_op,
  input  wire [31:0] alu_src1,
  input  wire [31:0] alu_src2,
  output wire [31:0] alu_result,
  output wire [31:0] mulcal
);

wire op_add;   //add operation
wire op_sub;   //sub operation
wire op_slt;   //signed compared and set less than
wire op_sltu;  //unsigned compared and set less than
wire op_and;   //bitwise and
wire op_nor;   //bitwise nor
wire op_or;    //bitwise or
wire op_xor;   //bitwise xor
wire op_sll;   //logic left shift
wire op_srl;   //logic right shift
wire op_sra;   //arithmetic right shift
wire op_lui;   //Load Upper Immediate
wire op_mul;//有符号乘
wire op_mulh;
wire op_mulu;//无符号乘
wire op_div;//有符号除
wire op_divu;//无符号除
wire op_mod;//有符号取模
wire op_modu;//无符号取模

// control code decomposition
assign op_add  = alu_op[ 0];
assign op_sub  = alu_op[ 1];
assign op_slt  = alu_op[ 2];
assign op_sltu = alu_op[ 3];
assign op_and  = alu_op[ 4];
assign op_nor  = alu_op[ 5];
assign op_or   = alu_op[ 6];
assign op_xor  = alu_op[ 7];
assign op_sll  = alu_op[ 8];
assign op_srl  = alu_op[ 9];
assign op_sra  = alu_op[10];
assign op_lui  = alu_op[11];
assign op_mul  = alu_op[12];//有符号乘
assign op_mulh = alu_op[13];//有符号乘取高位
assign op_mulu = alu_op[14];//无符号乘
assign op_div  = alu_op[15];//有符号除
assign op_divu = alu_op[16];//无符号除
assign op_mod  = alu_op[17];//有符号取模
assign op_modu = alu_op[18];//无符号取模


wire [31:0] add_sub_result;
wire [31:0] slt_result;
wire [31:0] sltu_result;
wire [31:0] and_result;
wire [31:0] nor_result;
wire [31:0] or_result;
wire [31:0] xor_result;
wire [31:0] lui_result;
wire [31:0] sll_result;

wire [63:0] mul_result;
wire [63:0] mulu_result;
wire [31:0] div_result;
wire [31:0] divu_result;
wire [31:0] mod_result;
wire [31:0] modu_result;

wire [63:0] sr64_result;
wire [31:0] sr_result;


// 32-bit adder
wire [31:0] adder_a;
wire [31:0] adder_b;
wire        adder_cin;
wire [31:0] adder_result;
wire        adder_cout;
wire div_res_valid;
wire divu_res_valid;

reg mulcal_r = 0;
reg divcal_r = 0;
reg divucal_r = 0;
reg [2:0] mulstatus = 0;
reg [1:0] divstatus = 0;
reg [1:0] divustatus = 0;
reg div_valid = 0;
reg divu_valid = 0;
assign mulcal = mulcal_r | divcal_r | divucal_r;
assign adder_a   = alu_src1;
assign adder_b   = (op_sub | op_slt | op_sltu) ? ~alu_src2 : alu_src2;  //src1 - src2 rj-rk
assign adder_cin = (op_sub | op_slt | op_sltu) ? 1'b1      : 1'b0;
assign {adder_cout, adder_result} = adder_a + adder_b + adder_cin;

// ADD, SUB result
assign add_sub_result = adder_result;

mult_signed ms(
  .CLK(clk),
  .A(alu_src1),
  .B(alu_src2),
  .P(mul_result)
);
mult_unsigned mus(
  .CLK(clk),
  .A(alu_src1),
  .B(alu_src2),
  .P(mulu_result)
);
div_signed ds(
  .s_axis_divisor_tdata(alu_src2),
  .s_axis_divisor_tvalid(div_valid),
  .s_axis_dividend_tdata(alu_src1),
  .s_axis_dividend_tvalid(div_valid),
  .aclk(clk),
  .m_axis_dout_tdata({div_result,mod_result}),
  .m_axis_dout_tvalid(div_res_valid)
);
div_unsigned dus(
  .s_axis_divisor_tdata(alu_src2),
  .s_axis_divisor_tvalid(divu_valid),
  .s_axis_dividend_tdata(alu_src1),
  .s_axis_dividend_tvalid(divu_valid),
  .aclk(clk),
  .m_axis_dout_tdata({divu_result,modu_result}),
  .m_axis_dout_tvalid(divu_res_valid)
);

always@(negedge clk)begin
  if(reset)begin
    mulcal_r <= 0;
    mulstatus <= 0;
  end else begin
    if((op_mul | op_mulh | op_mulu) && mulstatus == 3'b000)begin
        mulcal_r <= 1;
        mulstatus <= 3'b101;
    end
    else if(mulstatus == 3'b101)begin
        mulstatus <= 3'b100;
    end
    else if(mulstatus == 3'b100)begin
        mulstatus <= 3'b011;
    end
    else if(mulstatus == 3'b011)begin
        mulstatus <= 3'b010;
    end
    else if(mulstatus == 3'b010)begin
        mulstatus <= 3'b001;
    end
    else if(mulstatus == 3'b001)begin
        mulcal_r <= 0;
        mulstatus <= 3'b000;
    end
  end
end

always@(negedge clk)begin
  if(reset)begin
    divcal_r <= 0;
    divstatus <= 0;
    div_valid <= 0;
  end else begin
    if((op_div | op_mod) && divstatus == 0)begin
      divcal_r <= 1;
      div_valid <= 1;
      divstatus <= 1;
    end
    if(div_res_valid == 0 && divstatus == 1)begin
      divstatus <= 2;
    end
    if(div_res_valid == 1 && divstatus == 2)begin
      divcal_r <= 0;
      div_valid <= 0;
      divstatus <= 0;
    end
  end
end
always@(negedge clk)begin
  if(reset)begin
      divu_valid <= 0;
      divucal_r <= 0;
      divustatus <= 0;
  end else begin
    if((op_divu | op_modu) && divustatus == 0)begin
      divucal_r <= 1;
      divu_valid <= 1;
      divustatus <= 1;
    end
    if(divu_res_valid == 0 && divustatus == 1)begin
      divustatus <= 2;
    end
    if(divu_res_valid == 1 && divustatus == 2)begin
      divucal_r <= 0;
      divu_valid <= 0;
      divustatus <= 0;
    end
  end
end

// SLT result
assign slt_result[31:1] = 31'b0;   //rj < rk 1
assign slt_result[0]    = (alu_src1[31] & ~alu_src2[31])
                        | ((alu_src1[31] ~^ alu_src2[31]) & adder_result[31]);

// SLTU result
assign sltu_result[31:1] = 31'b0;
assign sltu_result[0]    = ~adder_cout;

// bitwise operation
assign and_result = alu_src1 & alu_src2;
assign or_result  = alu_src1 | alu_src2;
assign nor_result = ~or_result;
assign xor_result = alu_src1 ^ alu_src2;
assign lui_result = alu_src2;

// SLL result
assign sll_result = alu_src1 << alu_src2;   //rj << i5

// SRL, SRA result
assign sr64_result = {{32{op_sra & alu_src1[31]}}, alu_src1[31:0]} >> alu_src2[4:0]; //rj >> i5

assign sr_result   = sr64_result[31:0];

// final result mux
assign alu_result = ({32{op_add|op_sub}} & add_sub_result)
                  | ({32{op_slt       }} & slt_result)
                  | ({32{op_sltu      }} & sltu_result)
                  | ({32{op_and       }} & and_result)
                  | ({32{op_nor       }} & nor_result)
                  | ({32{op_or        }} & or_result)
                  | ({32{op_xor       }} & xor_result)
                  | ({32{op_lui       }} & lui_result)
                  | ({32{op_sll       }} & sll_result)
                  | ({32{op_srl|op_sra}} & sr_result)
                  | ({32{op_mul       }} & mul_result[31:0])
                  | ({32{op_mulh      }} & mul_result[63:32])
                  | ({32{op_mulu      }} & mulu_result[63:32])
                  | ({32{op_div      }}  & div_result)
                  | ({32{op_divu      }} & divu_result)
                  | ({32{op_mod      }}  & mod_result)
                  | ({32{op_modu      }} & modu_result)
                  
                  ;

endmodule
